package net.gdface.annotations;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;

import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;

import net.gdface.reflection.MethodUtils;

/**
 * @author guyadong
 * @since 2.7.0
 */
public class ActiveOnClassSupport {
	/**
	 * 创建 {@code clazz} 指定的实例<br>
	 * 如果从类中获取{@link ActiveOnClass}注解失败或注解中name字段指定的类不存在则代表子类依赖的库不存在，返回null
	 * @param clazz
	 */
	public static <T>T createInstanceIfActiveClassPresent(Class<T> clazz){
		if(isActive(clazz)){
			try {
				return clazz.newInstance();
			} catch (Exception e) {
			}
		}
		return null;
	}
	/**
	 * 调用 {@link AnnotatedElement#getAnnotation(Class)}从 {@link AnnotatedElement}对象中 
	 * 获取 {@link ActiveOnClass}注解，
	 * 抛出异常或注解中name字段指定的类不存在则返回{@code null}
	 * @param element
	 * @since 3.1.0
	 */
	public static ActiveOnClass getActiveOnClass(AnnotatedElement element) {
		try {
			ActiveOnClass annot = null == element? null : element.getAnnotation(ActiveOnClass.class);
			if(null != annot){
				for(String className:annot.name()){
					Class.forName(className);
				}
			}
			return annot;
		} catch (Throwable e) {
			return null;
		}
	}
	private static final Function<String,Class<?>> NAME_TO_CLASS_FUN = new Function<String, Class<?>>() {

		@Override
		public Class<?> apply(String input) {
			try {
				return Class.forName(input);
			} catch (ClassNotFoundException e) {
				// DEAD CODE
				throw new RuntimeException(e);
			}
		}
	};
	/**
	 * 则返回注解中定义的类集合，否则返回空集合
	 * @param annot
	 * @since 3.1.0
	 */
	public static Set<Class<?>> getClassesInActiveOnClass(ActiveOnClass annot){
		if(null != annot) {
			return FluentIterable.from(annot.value())
					.append(FluentIterable.from(annot.name()).transform(NAME_TO_CLASS_FUN)).toSet();
		}
		return Collections.emptySet();
	}
	/**
	 * 如果{@link AnnotatedElement}定义了{@link ActiveOnClass}注解，则返回注解中定义的类集合，否则返回空集合
	 * @param element
	 * @since 3.1.0
	 */
	public static Set<Class<?>> getClassesInActiveOnClass(AnnotatedElement element){
		return getClassesInActiveOnClass(getActiveOnClass(element));
	}
	/**
	 * 检查指定的对象是否为{@link ActiveOnClass}注解中定义的类型<br>
	 * @param annot 为{@code null}返回{@code true}
	 * @param object 为{@code null}返回{@code false}
	 * @since 3.1.0
	 */
	public static boolean isActive(ActiveOnClass annot,Object object){
		if(null != annot){
			if(null != object) {
				for(Class<?> clazz:annot.value()){
					if(clazz.isInstance(object)){
						return true;
					}
				}
				for(String className:annot.name()){
					if(className.equals(object.getClass().getName())){
						return true;
					}
				}
			}
			return false;
		}
		return true;
	}
	/**
	 * 如果{@link AnnotatedElement}对象上定义了{@link ActiveOnClass}注解，
	 * 则检查指定的对象是否为{@link ActiveOnClass}注解中定义的类型
	 * @param element 为{@code null}返回{@code true}
	 * @param object 为{@code null}返回{@code false}
	 * @see #isActive(ActiveOnClass, Object)
	 * @see #getActiveOnClass(AnnotatedElement)
	 * @since 3.1.0
	 */
	public static boolean isActive(AnnotatedElement element,Object object){
		return isActive(getActiveOnClass(element),object);
	}

	/**
	 * 基于{@link ActiveOnClass}注解判断指定的 {@link AnnotatedElement}实例是否是可激活的 <br>
	 * 如果获取{@link ActiveOnClass}注解失败或注解中name字段指定的类不存在则代表子类依赖的库不存在，
	 * 返回{@code false}，否则返回{@code true}
	 * 
	 * @param element 为{@code null}返回{@code false}
	 * @since 2.8.0
	 */
	public static boolean isActive(AnnotatedElement element) {
		return null != getActiveOnClass(element);
	}

	/**
	 * 基于{@link ActiveOnClass}注解判断参数指定的方法是否是可激活的 <br>
	 * @param clazz 方法所在类
	 * @param methodName 方法名
	 * @param parameterTypes 方法参数类型
	 * @throws NoSuchMethodException 没有找到指定的方法
	 * @since 3.1.0
	 * @see #isActive(AnnotatedElement)
	 * @see MethodUtils#getMatchingAccessibleMethod(Class, String, Class[])
	 */
	public static boolean isActive(Class<?> clazz, String methodName, Class<?>... parameterTypes)
			throws NoSuchMethodException {
		Method method = MethodUtils.getMatchingAccessibleMethod(clazz, methodName, parameterTypes,true,null);
		return null != getActiveOnClass(method);
	}
}
